# -*- coding: utf-8 -*-
'''
#----------------------------------------------------------------------------------------
# 功能：python工具库
#----------------------------------------------------------------------------------------
'''
import contextlib
import os
import platform
import select
import shlex
import socket
import subprocess
import sys
import threading
import time

from flib import chardet

class dict_to_object(dict):
    """docstring for dict_to_object"""
    def __getattr__(self, key):
        try:
            return self[key]
        except :
            return ''
        pass

    def __setattr__(self, key, value):
        self[key] = value
        pass

def getEncoding(s):
    try:
        enc = chardet.detect(s)
        return enc
    except:
        return

def toUnicode(s):
    if not s:
        return
    if isinstance(s, unicode):
        return s
    try:
        enc = chardet.detect(s)['encoding']
        #print enc
        if enc == 'ISO-8859-2' or enc == "TIS-620":
            enc = 'gbk'
            return unicode(s, enc, 'ignore')
    except Exception, e:
        print e
        return
    ###
    charsets = ('gbk', 'gb18030', 'gb2312', 'iso-8859-1', 'utf-16', 'utf-8', 'utf-32', 'ascii')
    for charset in charsets:
        try:
            #print charset
            return unicode(s, charset)
        except:
            continue

def toUTF8(s):
    try:
        if not isinstance(s, unicode) and not isinstance(s, str) and not isinstance(s, bytes):
            return
        if isinstance(s, unicode):
            return s.encode('utf-8')
        enc = chardet.detect(s)['encoding']
        if enc == 'utf-8':
            return s
        else:
            s = toUnicode(s)
            if s:
                return s.encode('utf-8')
    except Exception as e:
        print e
        return

def toGB2312(s):
    try:
        enc = getEncoding(s)
        if enc and enc['encoding'] == "utf-8":
            s = s.decode('utf-8')
    except Exception as e:
        pass
    s = toUnicode(s)
    if s:
        return s.encode('gb2312')

def toGBK(s):
    try:
        enc = getEncoding(s)
        if enc and enc['encoding'] == "utf-8":
            s = s.decode('utf-8')
    except Exception as e:
        pass
    s = toUnicode(s)
    if s:
        return s.encode('gbk')

def toStr(s):
    if not s: return ""
    try:
        if not isinstance(s, unicode) and not isinstance(s, str) and not isinstance(s, bytes):
            return str(s)
        if sys.stdout.encoding == "cp936":
            return toGBK(s)
        return toUTF8(s)
    except Exception as e:
        print str(e)
        return s

# 环境变量
def getenv(key, default=None):
    return os.environ[key] if key in os.environ else default
def setenv(key, v):
    os.environ[key] = v

# 全局唯一uuid
def newuuid():
    import uuid
    return uuid.uuid1()

# 日志输出
def __print__(s, newLine = True, color = None):
    from flib.tools.console_util import print_with_color
    print_with_color(s, newLine=newLine, color=color)

def __log__(tag, color, *args):
    if tag: __print__(toStr(tag), newLine=False, color=color)
    __index__ = 0
    for s in args:
        __index__ = __index__ + 1
        tmp = toStr(s)
        __print__(tmp if tmp else str(s), newLine=False, color=color)
        __print__(" " if __index__ != len(args) else "", newLine=False, color=color)
    __print__("", newLine=True, color=color)
    sys.stdout.flush()

class Log(object):
    """log util"""
    _lock = threading.Lock()
    @staticmethod
    def log(*args):
        Log._lock.acquire()
        __log__("", None, *args)
        Log._lock.release()
    @staticmethod
    def i(*args):
        Log._lock.acquire()
        __log__("[info]", None, *args)
        Log._lock.release()
    @staticmethod
    def d(*args):
        Log._lock.acquire()
        __log__("[debug]", "blue", *args)
        Log._lock.release()
    @staticmethod
    def w(*args):
        Log._lock.acquire()
        __log__("[warning]", "yellow", *args)
        Log._lock.release()
    @staticmethod
    def e(*args):
        Log._lock.acquire()
        __log__("[error]", "red", *args)
        Log._lock.release()
    @staticmethod
    def s(*args):
        Log._lock.acquire()
        __log__("[session]", "green", *args)
        Log._lock.release()
    @staticmethod
    def expt(*args):
        Log._lock.acquire()
        __log__("[exception]", "red", *args)
        Log._lock.release()

def ZLPrint(*args):
    __log__("", None, *args)

#quit
def quit(func):
    def quit_call(*arg, **kw):
        result = func(*arg, **kw)
        if not result:
            exit(-1)
        return result
    return quit_call

def execute(cmds="", logout=True, finalcallback=None):
    def unbuffered(proc, stream='stdout'):
        newlines = ['\n', '\r\n', '\r']
        stream = getattr(proc, stream)
        with contextlib.closing(stream):
            while True:
                out = []
                last = stream.read(1)
                # Don't loop forever
                if not last or last == '' and proc.poll() is not None:
                    break
                while last not in newlines:
                    # Don't loop forever
                    if last == '' and proc.poll() is not None:
                        break
                    out.append(last)
                    last = stream.read(1)
                out = ''.join(out)
                yield out
    try:
        if logout: Log.log("---->  " + cmds)
        sysstr = platform.system()
        p = subprocess.Popen(shlex.split(cmds) if sysstr == "Windows" else cmds, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        result = []
        if sysstr == "Windows":
            for line in unbuffered(p):
                if line:
                    result.append(line)
                    if logout: Log.log (line.strip('\r\n'))
            for line in unbuffered(p, "stderr"):
                if line:
                    Log.e (line)
        else:
            rlist = [p.stdout,p.stderr]
            while rlist:
                readable, _, _ = select.select(rlist, [], [])
                for r in readable:
                    if r is p.stdout:
                        line = r.readline()
                        if not line:
                            rlist.remove(r)
                            continue
                        line = line.rstrip('\n').rstrip('\r\n')
                        if not line: continue
                        result.append(line)
                        if logout: Log.log (line)
                    elif r is p.stderr:
                        line = r.readline()
                        if not line:
                            rlist.remove(r)
                            continue
                        line = line.rstrip('\n').rstrip('\r\n')
                        if not line: continue
                        Log.e (line)
        p.wait()

        returncode = p.returncode

        if p.stdin:
            p.stdin.close()
        if p.stdout:
            p.stdout.close()
        if p.stderr:
            p.stderr.close()
        try:
            p.kill()
        except OSError:
            pass

        if returncode != 0:
            Log.e ("Failed to execute command '" + cmds + "'")
            return False
        else:
            if len(result) == 0: return True
            return result
    except Exception as e:
        Log.expt (str(e))
        Log.e ("Failed to execute command '" + cmds + "'")
        return False
    finally:
        if finalcallback: finalcallback()

def exec_sh(cmds="", logout=False, args=list()):
    name = str(newuuid()) + ".sh"
    with open(name, "w+") as f:
        f.write(cmds)
        f.close()
        param = " " + " ".join([str(x) for x in args]) if args else ""
        return execute(cmds="sh " + name + param, finalcallback=lambda:os.remove(name), logout=logout)

@quit
def safe_execute(cmds="", logout=True, finalcallback = None):
    return execute(cmds=cmds, logout=logout, finalcallback=finalcallback)

def exec_command(*args, **kwargs):
    param = " ".join([str(x) for x in args])
    return execute(param.format(**kwargs), logout=True, finalcallback=None)

def check_output(*args, **kwargs):
    param = " ".join([str(x) for x in args])
    return execute(param.format(**kwargs), logout=False, finalcallback=None)

# netcat 服务器
class nc_server(threading.Thread):
    """netcat for nc_server"""
    class nc_client(threading.Thread):
        def __init__(self, client_socket, server):
            super(nc_server.nc_client, self).__init__()
            self.socket = client_socket
            self.server = server
            self.lock = threading.Lock()
            self.stopped = False
        def __del__(self):
            self.stop()
        def stop(self):
            try:
                self.lock.acquire()
                self.stopped = True
                self.lock.release()
                self.socket.close()
            except:
                pass
        def run(self):
            while True:
                try:
                    self.lock.acquire()
                    if self.stopped:
                        break
                    data = self.socket.recv(1024)
                    recv_len = len(data)
                    if recv_len == 0:
                        __print__("")
                        break
                    tmp = toStr(data) or data
                    __print__(tmp.strip('\r\n').strip('\n'), True if recv_len < 1024 else False)
                except:
                    time.sleep(0.01)
                    pass
                finally:
                    self.lock.release()
            Log.i("client_handler finished")

    def __init__(self, host="0.0.0.0", port=0):
        super(nc_server, self).__init__()
        self.stopped = False
        self.clients = []
        self.host = host
        self.port = port
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((host, port))
        self.server.listen(0)
        self.server.setblocking(0)
        self.lock = threading.Lock()
        Log.i("start nc server [{0}:{1}]!".format(host, self.get_port()))

    def get_port(self):
        a = self.server.getsockname()
        self.port = a[1]
        return self.port

    def get_ip(self):
        a = self.server.getsockname()
        self.host = a[0]
        return self.host

    def stop(self):
        self.lock.acquire()
        self.stopped = True
        self.lock.release()
        Log.i("stop netcat.")

    def isStoped(self):
        self.lock.acquire()
        try:
            return self.stopped
        finally:
            self.lock.release()

    def run(self):
        while True:
            try:
                self.lock.acquire()
                if self.stopped:
                    break
                client_socket, addr = self.server.accept()
                client_socket.setblocking(0)
                Log.i("accept client:{0}".format(addr))
                client_thread = nc_server.nc_client(client_socket, self) #threading.Thread(target=nc_server.client_handler, args=(client_socket, self))
                client_thread.start()
                self.clients.append(client_thread)
            except Exception as e:
                time.sleep(0.01)
                pass
            finally:
                self.lock.release()

        self.server.close()
        for subthread in self.clients:
            subthread.stop()


#测试
def main():
    print sys.stdout.encoding
    exec_sh("svn info $1", args=["F:/Seven/ElementUnityWin"])
    exec_command('''svn info "{path}"''', path="F:/Seven/ElementUnityWin")
    #safe_execute('''svn info "F:/Seven/ElementUnityWin"''', logout=True)
    # print platform.system()
    Log.log("b", "a")
    # __log__('',"为什么")
    # print toStr("为什么")
    #nc_server().start()

if __name__ == '__main__':
    main()
